package zaplog

import (
	"fmt"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"log"
	"time"
)

const (
	lowFileName  = "/runtime.log"
	highFileName = "/error.log"
)

func defaultConfig() configEntry {
	return configEntry{
		IsDebug:       true,
		TimePrecision: "millisecond",
		TextFormat:    "console",
		LogPath:       "logs",
		MaxSize:       10,
		MaxBackups:    15,
		MaxAge:        30,
		Compress:      true,
		HookFunc: func(entry zapcore.Entry) error {
			return nil
		},
	}
}

func GenerateZap(options ...OptFunc) *zap.Logger {
	zapOption := defaultConfig()
	for _, opt := range options {
		opt(&zapOption)
	}

	//开发调试情况下
	if zapOption.IsDebug {
		if logger, err := zap.NewDevelopment(zap.Hooks(zapOption.HookFunc)); err != nil {
			log.Fatalf("初始化日志失败，%v\n", err)
		} else {
			return logger
		}
	}

	//测试生成情况下
	encoderConfig := zap.NewProductionEncoderConfig()

	var recordTimeFormat string
	switch zapOption.TimePrecision {
	case "second":
		recordTimeFormat = "2006-01-02 15:04:05"
	case "millisecond":
		recordTimeFormat = "2006-01-02 15:04:05.000"
	default:
		recordTimeFormat = "2006-01-02 15:04:05"
	}

	encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format(recordTimeFormat))
	}
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	encoderConfig.TimeKey = "created_at" // 生成json格式日志的时间键字段，默认为 ts,修改以后方便日志导入到 ELK 服务器

	var encoder zapcore.Encoder
	switch zapOption.TextFormat {
	case "console":
		encoder = zapcore.NewConsoleEncoder(encoderConfig) // 普通模式
	case "json":
		encoder = zapcore.NewJSONEncoder(encoderConfig) // json格式
	default:
		encoder = zapcore.NewConsoleEncoder(encoderConfig) // 普通模式
	}

	highPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		return level >= zap.ErrorLevel
	})

	lowPriority := zap.LevelEnablerFunc(func(level zapcore.Level) bool {
		return level < zap.ErrorLevel && level > zap.DebugLevel
	})

	highWriter := zapcore.AddSync(&lumberjack.Logger{
		Filename:   fmt.Sprintf("%s%s", zapOption.LogPath, highFileName), //日志文件的位置
		MaxSize:    zapOption.MaxSize,                                    //在进行切割之前，日志文件的最大大小（以MB为单位）
		MaxBackups: zapOption.MaxBackups,                                 //保留旧文件的最大个数
		MaxAge:     zapOption.MaxAge,                                     //保留旧文件的最大天数
		Compress:   zapOption.Compress,                                   //是否压缩/归档旧文件
	})

	lowWriter := zapcore.AddSync(&lumberjack.Logger{
		Filename:   fmt.Sprintf("%s%s", zapOption.LogPath, lowFileName), //日志文件的位置
		MaxSize:    zapOption.MaxSize,                                   //在进行切割之前，日志文件的最大大小（以MB为单位）
		MaxBackups: zapOption.MaxBackups,                                //保留旧文件的最大个数
		MaxAge:     zapOption.MaxAge,                                    //保留旧文件的最大天数
		Compress:   zapOption.Compress,                                  //是否压缩/归档旧文件
	})

	// 开始初始化zap日志核心参数，
	//参数一：编码器
	//参数二：写入器
	//参数三：参数级别，debug级别支持后续调用的所有函数写日志，如果是 fatal 高级别，则级别>=fatal 才可以写日志
	highZapCore := zapcore.NewCore(encoder, highWriter, highPriority)
	lowZapCore := zapcore.NewCore(encoder, lowWriter, lowPriority)
	return zap.New(zapcore.NewTee(highZapCore, lowZapCore), zap.AddCaller(), zap.Hooks(zapOption.HookFunc))
}
